#include "base64.h"

#define B2A(c)                                                                 \
  (((c) >= 'A' && (c) <= 'Z')                                                  \
       ? ((c) - 'A')                                                           \
       : ((c) >= 'a' && (c) <= 'z')                                            \
             ? ((c) - 'a' + 26)                                                \
             : ((c) >= '0' && (c) <= '9')                                      \
                   ? ((c) - '0' + 52)                                          \
                   : ((c) == '+') ? 62 : ((c) == '/') ? 63 : 0)

#define A2B(c)                                                                 \
  (((c) >= 0 && (c) < 26)                                                      \
       ? ((c) + 'A')                                                           \
       : ((c) < 52) ? ((c)-26 + 'a')                                           \
                    : ((c) < 62) ? ((c)-52 + '0') : ((c) == 62) ? '+' : '/')

int base64_decode(unsigned char *out, unsigned char *in, int in_len)
{
  unsigned char c1, c2, c3, c4;
  int i, j;

  for (i = 0, j = 0; i < in_len; i += 4, j += 3) {
    c1 = B2A(in[i]);
    c2 = B2A(in[i + 1]);
    c3 = B2A(in[i + 2]);
    c4 = B2A(in[i + 3]);
    out[j] = (c1 << 2) | ((c2 >> 4) & 0x3);
    out[j + 1] = (c2 << 4) | ((c3 >> 2) & 0xf);
    out[j + 2] = (c3 << 6) | c4;
    if (in[i + 2] == '=') {
      j += 1;
      break;
    } else if (in[i + 3] == '=') {
      j += 2;
      break;
    }
  }

  return j;
}

int base64_encode(unsigned char *out, unsigned char *in, int in_len)
{
  int i, j, len;
  unsigned char c1, c2, c3, c4;

  len = in_len / 3 * 3;

  for (i = 0, j = 0; i < len; i += 3, j += 4) {
    c1 = A2B(in[i] >> 2);
    c2 = A2B(((in[i] & 0x3) << 4) | ((in[i + 1]) >> 4));
    c3 = A2B(((in[i + 1] & 0xf) << 2) | (in[i + 2]) >> 6);
    c4 = A2B(in[i + 2] & 0x3f);
    out[j] = c1;
    out[j + 1] = c2;
    out[j + 2] = c3;
    out[j + 3] = c4;
  }
  if ((in_len % 3) == 1) {
    c1 = A2B(in[i] >> 2);
    c2 = A2B((in[i] & 0x3) << 4);
    out[j] = c1;
    out[j + 1] = c2;
    out[j + 2] = '=';
    out[j + 3] = '=';
    j += 4;
  } else if ((in_len % 3) == 2) {
    c1 = A2B(in[i] >> 2);
    c2 = A2B(((in[i] & 0x3) << 4) | ((in[i + 1]) >> 4));
    c3 = A2B((in[i + 1] & 0xf) << 2);
    out[j] = c1;
    out[j + 1] = c2;
    out[j + 2] = c3;
    out[j + 3] = '=';
    j += 4;
  }

  return j;
}
